﻿using System;
using System.Collections.Generic;
using System.Management.Automation;

using CrystalDecisions.Enterprise.Desktop;

namespace BO.Management.PowerShell
{
    [Cmdlet(VerbsLifecycle.Stop, "JobServer", DefaultParameterSetName = CONNECT_SECWINAD)]
    public class StopJobServer : BaseActionObject<ServerProvider, Server>
    {

        #region "Constantes"
        /// <summary>
        /// Defautl timeout to retrieve the state of the job server is 60 secondes
        /// </summary>
        public const Int32 DEFAULT_TIMEOUT = 10;

        /// <summary>
        /// pause to wait having the server status is 2 second, 2000 ms
        /// </summary>
        public const Int32 PAUSE_WAITING_STATUS = 2000;
        #endregion

        #region "Powershell parameters"
        /// <summary>
        /// Timeout to query the last server status
        /// </summary>
        [Parameter()]
        public Int32? Timeout { get; set; }


        /// <summary>
        /// Timeout to query the last server status
        /// </summary>
        [Parameter()]
        public SwitchParameter Force { get; set; }

        #endregion

        public override ServerProvider GetProvider()
        {
            return new ServerProvider(this.LogonProvider);
        }

        protected override void BeginProcessing()
        {
            base.BeginProcessing();
            //TODO : Add check on parameters
            if (!this.Timeout.HasValue)
            {
                this.Timeout = DEFAULT_TIMEOUT;
            }
        }


        public override void WorkWithSingleObject(Server boeObject)
        {
            if (boeObject == null)
            {
                this.ThrowTerminatingErrorWithExitCode(BOErrorCode.StopServerFailure, "Unable to stop the server");
                return;
            }


            //protect the CMS : TODO add switch to forece stopping CMS
            if (boeObject.Title.ToLower().Contains("centralmanagementserver"))
            {
                WriteObject("Unable to stop a CMS");
                return;
            }

            //First disable the server
            if (!boeObject.Disabled)
            {
                WriteVerbose(String.Format("The Server {0} will be disabled", boeObject.Title));
                boeObject.Disabled = true;
                BoeProvider.Commit(boeObject);
            }
            else
            {
                WriteVerbose(String.Format("The Server {0} is already disabled", boeObject.Title));
            }

            //Wait CurrentDisable at true : max wait 3 tests
            Int16 nbTest = 0;
            while (boeObject.CurrentDisabledState & nbTest < 3)
            {
                WriteVerbose(String.Format("The Server {0} is not yet disabled", boeObject.Title));
                System.Threading.Thread.Sleep(1000);
                boeObject = this.ProcessById(boeObject.ID);
                nbTest++;
            }

            if (boeObject.ServerIsAlive)
            {
                WriteVerbose(String.Format("The Server {0} will be stopped", boeObject.Title));
                boeObject.ManageServer(CrystalDecisions.Enterprise.Desktop.CeServerManageOp.ceServerStop);
            }
            else
            {
                WriteVerbose(String.Format("The Server {0} is already stopped", boeObject.Title));
            }
        }


        public override void WorkWithListObjects(List<Server> boeObjects)
        {
            List<Server> cmsServer = new List<Server>();

            //Work for all Job Server which is not a CentralManagementServer
            foreach (Server server in boeObjects)
            {
                if (server.Title.Contains("CentralManagementServer"))
                {
                    WriteVerbose(String.Format("The Central Management Server is in the list of stopping server: {0}", server.Title));
                    cmsServer.Add(server);
                }
                else
                {
                    this.WorkWithSingleObject(server);
                }
            }
                  
            //Do we need to stop the Central Management Server ?
            foreach (Server server in cmsServer)
            {
                //this.WorkWithSingleObject(server);
                this.WriteObject("Central Management Server can only be stopped by using specifc name");
            }

        }


        protected override void EndProcessing()
        {
            Boolean startServers = true;
            foreach (Int32 serverId in this.OutputObjectIds)
            {
                if (!this.GetFinalStatus(serverId))
                    startServers = false;
            }

            if (!startServers)
            {
                String message = this.OutputObjectIds.Count > 1 ? "Unable to stop list of servers" : "Unable to stop server";
                this.ThrowTerminatingErrorWithExitCode(BOErrorCode.StopServerFailure, message);
            }

            base.EndProcessing();
        }

        private Boolean GetFinalStatus(Int32 serverId)
        {
            //Read the job server to have its final status
            WriteVerbose(String.Format("Read the final status from the cms for the Server with its id {0}", serverId));

            Boolean searchActive = true;
            DateTime serverStatusQueryStarted = DateTime.Now;

            while (searchActive)
            {

                using (LogonProvider logonProvider = new LogonProvider())
                {
                    this.GetSession(logonProvider);
                    using (ServerProvider serverProvider = new ServerProvider(logonProvider))
                    {
                        serverProvider.AddFilterToId(QueryBuilder.OperatorType.Equal, serverId);
                        Server server = serverProvider.GetObject(serverId);
                        if (server == null)
                        {
                            this.ThrowTerminatingErrorWithExitCode(BOErrorCode.GenericError, String.Format("Unable to find the server with its id {0}", serverId));
                            return false;
                        }
                        else
                        {
                            //If server is alive, wait PAUSE_WAITING_STATUS before query the next state
                            if (server.ServerIsAlive)
                            {
                                this.WriteVerbose(String.Format("The server {0} has not yet stopped, wait {1} ms for next query", server.Title, PAUSE_WAITING_STATUS));
                                System.Threading.Thread.Sleep(PAUSE_WAITING_STATUS);
                            }
                            else
                            {
                                this.WriteObject(server);
                                Console.WriteLine(String.Format("Stop JobServer {0} : {1}", server.Title, !server.ServerIsAlive));
                                return (!server.ServerIsAlive);
                            }

                            if (this.Timeout.HasValue)
                            {

                                if (serverStatusQueryStarted.AddSeconds(this.Timeout.Value) < DateTime.Now)
                                {
                                    if (this.Force)
                                    {
                                        this.Force = false;
                                        try
                                        {
                                            BoeProvider.Kill(server);
                                        }
                                        catch (Exception ex)
                                        {
                                            this.WriteError(BOErrorManagement.GetErrorRecord(BOErrorCode.StopServerFailure,
                                                String.Format("Unable to force stopping the server {0}", server.Title), ex));
                                        }
                                    }
                                    else
                                    {

                                        this.WriteError(BOErrorManagement.GetErrorRecord(BOErrorCode.TimeOut,
                                            String.Format("Timeout occured to retrieve the state after stop this server {0} at {1}", server.Title, DateTime.Now)));
                                        return false;
                                    }
                                }
                            }//End of timeout treatement


                        }
                    }//End of ServerProvider
                }//End of logon provider
            }//End of SearchActive

            return false;
        }

    }

}
